package cn.rocksolid.proxy.config;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import cn.rocksolid.sport.common.Constant.ShiroConst;
import cn.rocksolid.sport.common.http.RockSolidAuthFilter;
import cn.rocksolid.sport.common.http.RockSolidContextFilter;
import cn.rocksolid.sport.common.http.RockSolidHttpCorsConfig;

/**
 * Shiro configurations
 *
 * @author Axl Zhao
 * @email axl.zhao@163.com
 */
@Configuration
public class ShiroConfig {

  private static final String RS_CTX = "rsctx";
  private static final String RS_AUTH = "rsauth";

  @Bean(ShiroConst.SESSION_MANAGER_KEY)
  public SessionManager sessionManager() {
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    sessionManager.setSessionValidationSchedulerEnabled(true);
    sessionManager.setSessionIdCookieEnabled(true);
    return sessionManager;
  }

  @Bean(ShiroConst.SECURITY_MANAGER_KEY)
  public SecurityManager securityManager(ShiroRealm shiroRealm, SessionManager sessionManager) {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(shiroRealm);
    securityManager.setSessionManager(sessionManager);

    return securityManager;
  }

  @Bean
  public FilterRegistrationBean<DelegatingFilterProxy> shiroFilterRegistration() {
    FilterRegistrationBean<DelegatingFilterProxy> registration = new FilterRegistrationBean<>();
    registration.setFilter(new DelegatingFilterProxy(ShiroConst.FILTER_KEY));
    // 该值缺省为false，表示生命周期由SpringApplicationContext管理，设置为true则表示由ServletContainer管理
    registration.addInitParameter("targetFilterLifecycle", "true");
    registration.setEnabled(true);
    registration.setOrder(Integer.MAX_VALUE - 1);
    registration.addUrlPatterns("/*");
    return registration;
  }

  @Bean(ShiroConst.FILTER_KEY)
  public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager, RockSolidHttpCorsConfig corsConfig) {
    ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
    shiroFilter.setSecurityManager(securityManager);

    Map<String, Filter> filters = new HashMap<>();
    filters.put(RS_CTX, new RockSolidContextFilter());
    filters.put(RS_AUTH, new RockSolidAuthFilter(corsConfig));
    shiroFilter.setFilters(filters);

    Map<String, String> filterMap = new HashMap<>();
    filterMap.put("/sys/lookup", RS_CTX);
    filterMap.put("/sys/login", RS_CTX);
    filterMap.put("/captcha.jpg", RS_CTX);
    filterMap.put("/**", String.join(",", RS_CTX, RS_AUTH));
    shiroFilter.setFilterChainDefinitionMap(filterMap);
    return shiroFilter;
  }

  @Bean(ShiroConst.LIFECYCLE_BEAN_POST_PROCESSOR_KEY)
  public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
  }

  // Below beans creation for enable Shiro Annotations
  @Bean
  public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
    proxyCreator.setProxyTargetClass(true);
    return proxyCreator;
  }

  @Bean
  public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    advisor.setSecurityManager(securityManager);
    return advisor;
  }
}
